package cn.zhangfusheng.elasticsearch.scan;

import cn.zhangfusheng.elasticsearch.annotation.document.IndexDescription;
import cn.zhangfusheng.elasticsearch.annotation.document.IndexSetting;
import cn.zhangfusheng.elasticsearch.annotation.document.IndexTransfer;
import cn.zhangfusheng.elasticsearch.exception.GlobalSystemException;
import cn.zhangfusheng.elasticsearch.repository.DefaultRepository;
import cn.zhangfusheng.elasticsearch.repository.ElasticSearchRepository;
import cn.zhangfusheng.elasticsearch.setting.SettingJsonConfig;
import cn.zhangfusheng.elasticsearch.transfer.TransferInfoRepository;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.filter.AbstractClassTestingTypeFilter;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * @author fusheng.zhang
 * @date 2022-02-24 13:25:00
 */
public class ElasticSearchEntityRepositoryFilter extends AbstractClassTestingTypeFilter {
    private final String interfaceClassName;
    private final SettingJsonConfig settingJsonConfig;
    private final List<ElasticSearchEntityRepositoryDetail> entityRepositoryDetails = new ArrayList<>();
    private final List<Class<?>> entityClassCache = new ArrayList<>();

    public ElasticSearchEntityRepositoryFilter(Class<?> aClass, SettingJsonConfig settingJsonConfig) {
        this.interfaceClassName = aClass.getName();
        this.settingJsonConfig = settingJsonConfig;
    }

    @Override
    protected boolean match(ClassMetadata metadata) {
        String[] interfaceNames = metadata.getInterfaceNames();
        boolean b = !metadata.getClassName().equals(ElasticSearchRepository.class.getName())
                && !metadata.getClassName().equals(DefaultRepository.class.getName())
                && !metadata.getClassName().equals(TransferInfoRepository.class.getName())
                && Arrays.asList(interfaceNames).contains(this.interfaceClassName);
        if (b) analysis(metadata);
        return b;
    }

    /**
     * 解析 Repository entity index version
     * @param metadata
     */
    @SuppressWarnings("unchecked")
    private void analysis(ClassMetadata metadata) {
        try {
            /*解析出 zingRepository 和 entity 的class*/
            // 接口的泛型
            Class<?> entityClass = null;
            Class<?> repositoryClass = Class.forName(metadata.getClassName());
            Type[] genericInterfaces = repositoryClass.getGenericInterfaces();
            if (genericInterfaces.length != 1) {
                throw new GlobalSystemException("接口::{}::继承的接口个数不等于1", repositoryClass.getName());
            }
            Type genericInterface = genericInterfaces[0];
            if (genericInterface instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) genericInterface;
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                if (actualTypeArguments.length != 1) {
                    throw new GlobalSystemException("接口::{}::的泛型个数不等于1", repositoryClass.getName());
                }
                Type actualTypeArgument = actualTypeArguments[0];
                if (actualTypeArgument instanceof Class) {
                    entityClass = (Class<?>) actualTypeArgument;
                }
            }
            if (Objects.isNull(entityClass)) {
                throw new GlobalSystemException("{}::解析失败,请检查书写是否符合规范", repositoryClass.getName());
            } else if (Objects.isNull(entityClass.getAnnotation(IndexDescription.class))) {
                throw new GlobalSystemException("{}::未添加注解 IndexDiscription", entityClass);
            }
            /*解析 entity 的index version*/
            IndexDescription indexDescription = entityClass.getAnnotation(IndexDescription.class);
            IndexTransfer indexTransfer = entityClass.getAnnotation(IndexTransfer.class);
            IndexSetting indexSetting = entityClass.getAnnotation(IndexSetting.class);
            String setting = settingJsonConfig.getSetting(Optional.ofNullable(indexSetting).isPresent() ? indexSetting.settingName() : null);
            ElasticSearchEntityRepositoryDetail elasticSearchEntityRepositoryDetail = new ElasticSearchEntityRepositoryDetail(
                    entityClassCache.contains(entityClass), (Class<? extends ElasticSearchRepository<?>>) repositoryClass,
                    entityClass, indexDescription, indexTransfer, setting);
            // 将数据迁移的操作单独处理
            entityRepositoryDetails.add(elasticSearchEntityRepositoryDetail);
            entityClassCache.add(entityClass);
        } catch (Exception e) {
            throw new GlobalSystemException(e);
        }
    }

    public List<ElasticSearchEntityRepositoryDetail> getEntityRepositoryDetails() {
        return this.entityRepositoryDetails;
    }
}
